#include "xMappingNode.h"
#include "xNodeType.h"
#include "xNodeType_composite.h"
#include "vmLibrary.h"
#include "dummyVM.h"
#include "WOP_Ref_Entry.h"
#include "WOPM.h"
#include "MappingEngine.h"
#include "xNodeType_VM_Factory.h"
#include "xOutMappingPort.h"
#include "xParameterPack.h"
#include "DIVA_VM.h"
#include "xInMappingPort.h"

#include <sstream>

void xNodeType_composite::setupPorts() {

	
	assert(_parent != NULL);
	assert(_vd != NULL);

	string mds = _vd->_compositebody_mds ;
	_body = xMappingTree::fromMDStoXMappingTree(mds, NULL); 
	_body->compute_level();  // compute the level 

	// set all the level 0 ports as the inports of this composite
	vector<xMappingNode*> nodes = _body->get_nodes_by_level(0);
	//assert(nodes.size() == _vd->in_para_no);
	for (unsigned int i = 0 ; i < nodes.size() ; i++ ) {
		vector<xInMappingPort *> inports = nodes[i]->getInports() ;
		assert(inports.size() == 1); // a gate should only has one inport
		xInMappingPort *p = inports[0];
		_parent->_inports.push_back(p) ;
	}

	this->outNodes_ = this->_body->get_output_nodes();
	// set all the outports of _root as the outport of this composite
	for( auto it = this->outNodes_.begin(); it != this->outNodes_.end(); ++it) {
		auto outports = ( *it )->getOutports();
		assert( outports.size() == 1 || !"invalid outport" );
		_parent->_outports.push_back( outports.front() );
	}

	// set the _owner attr of each mapping node
	vector<xMappingNode*> allnodes = _body->get_nodes();
	vector<xMappingNode *>::iterator itr ;
	for (itr = allnodes.begin(); itr != allnodes.end() ; itr++) {
		xMappingNode *p = (*itr);
		p->setCompositeParent(this->getParent());
	}

	
}

// this method is called by 
void xNodeType_composite::createMappedObject() {
   	vector<xMappingNode *>::iterator itr ;
	// _body.compute_level();  // we should not recompute the level here because now the gate already has child mapping node
				               // connecting to it.

	// level 0 should be all WOPs 
	// basically, we do not need to process anything.
	for (int i=0; i<= _body->get_max_level(); i++) {
		vector<xMappingNode*> nodes = _body->get_nodes_by_level(i);
		for (unsigned int j=0; j< nodes.size(); j++) {
			nodes[j]->getNodeTypePtr()->createMappedObject(); 
			// if a node is a VM node, it could create a VM object
			// The behaviors of the createMappedObject() depends on the type of node
		}
	}
}

xParameter xNodeType_composite::toXParameter(string toType, xOutMappingPort * linkedop) {
	xOutMappingPort * root_op = nullptr;
	xNodeType * np = nullptr;
	for( auto it = this->outNodes_.begin(); it != this->outNodes_.end(); ++it ) {
		auto p = *it;
		auto outports = p->getOutports();
		auto oit = std::find( outports.begin(), outports.end(), linkedop );
		if( oit != outports.end() ) {
			root_op = *oit;
			np = p->getNodeTypePtr();
			break;
		}
	}

	xParameter xp = np->toXParameter(root_op->getType(),root_op);
	return xp ;
}
bool xNodeType_composite::update() {
	vector<xMappingNode *>::iterator itr ;
	
	// level 0 should be all "gate" type mapping node
	for (int i=0; i<= _body->get_max_level(); i++) {
		vector<xMappingNode*> nodes = _body->get_nodes_by_level(i);
		for (unsigned int j=0; j< nodes.size(); j++) {
			nodes[j]->getNodeTypePtr()->update(); 
		}
	}
	return true ;
}
string xNodeType_composite::toMDString(MDSType type) {
	ostringstream MDSstr ;
	int inp_no = _vd->in_para_no ; 

	if ( type == xNodeType::NormalMDSFormat ){
		MDSstr << "node "<<  _parent->getSerialNo() << " composite " << _vd->_name << " " << inp_no << endl ;
	} else if ( type == xNodeType::ParameterizedMDSFormat){
		MDSstr << "ADD_NODE " <<"node "<< "SN" <<_parent->getSerialNo() << " composite " << _vd->_name << " " << inp_no << endl ;
	} else {
		assert(false);
	}
	
	MDSstr << " defaultstring<" ;

	xMappingNode *node = this->getParent();
	for (int i = 0 ; i < node->getInportSize(); i++) {
			// get the nodetype pointer that connects to this inport (unique)
			xInMappingPort *inp = node->getInPort(i);
			MDSstr << " " << inp->getDefaultValue();
	}
	MDSstr << " >" << endl ;
	return MDSstr.str();
}

int xNodeType_composite::parseMDString(vector<string>& tokens, int start_index) {
	int i = start_index ;

	assert(tokens[i] == "node");
	string serialno = tokens[i+1] ;
	assert(tokens[i+2] == "composite");
	string vmname = tokens[i+3] ; // nodetypes are "wop" "vm" "vmcollector"
	string inport_no = tokens[i+4] ;
	
	istringstream buf2(inport_no);
	int inp_no ;
	buf2 >> inp_no ;

	assert(tokens[i+5] == "defaultstring<") ;
	assert(_vd->in_para_no == inp_no) ;

	i+=6 ;
	xMappingNode* p = this->getParent();
	for (int k=0; k < p->getInportSize(); k++) {
		xInMappingPort* in = p->getInPort(k);
		in->setDefaultValue(tokens[i+k]);
	}
	assert(tokens[i+p->getInportSize()] == ">"); 
	return p->getInportSize()+7 ; // 5 tokens begins with "node", defaultstring< is one token
	                  // and > is the last tokens ;
}
string xNodeType_composite::valid_rhs_pair[][2] = { 
// in a VM node, the outport can only have type (int*, double*, tostring*	
"int*", "wop", 
"double*", "wop",
"dontcare", "tostring*"
};

bool xNodeType_composite::verify_rhs_link(xOutMappingPort *left, xInMappingPort *right, xNodeType* rightnode) {
	if (left->getType() == right->getType()) return true ;
	return check_valid_pair(valid_rhs_pair, sizeof(valid_rhs_pair)/(sizeof(string)*2),
							left->getType(), right->getType());
}
bool xNodeType_composite::verify_lhs_link(xOutMappingPort *left, xNodeType* leftnode, xInMappingPort *right) {
	
	return true ; // when a link is on the left hand side of this node
	              // we have the leftnode to check for the validity
}